 package com.hp.bon.sgw.service;
 
 import java.io.UnsupportedEncodingException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hp.bon.sgw.Constants;
import com.hp.bon.sgw.bean.AuthResult;
import com.hp.bon.sgw.bean.CapabilityCtrlBean;
import com.hp.bon.sgw.bean.LoginStatus;
import com.hp.bon.sgw.bean.SysParamBean;
import com.hp.bon.sgw.bean.XmlMessage;
import com.hp.bon.sgw.core.CallParam;
import com.hp.bon.sgw.domain.Node;
import com.hp.bon.sgw.start.SpringContext;
import com.hp.bon.sgw.util.RSAUtil;
 
 public class AuthControlService
   implements ConfigListener
 {
   private static AuthControlService instance;
   private static final Logger logger = LoggerFactory.getLogger(AuthControlService.class);
   private final Map<String, LoginStatus> tempDeniedNEMap = new ConcurrentHashMap<String, LoginStatus>();
   private volatile int loginFailedMaxTimes = 3;
   private volatile int tempDeniedSeconds = 120;
   private boolean disableAuth;
   protected final Set<String> monitorConfKeys = new HashSet<String>();
 
	public static AuthControlService getInstance() {
		if (instance == null) {
			instance = new AuthControlService();
			instance.init();
			
			SpringContext.getInstance().getConfigService().addConfigListener(instance);
		}
		return instance;
	}
 
   public AuthControlService()
   {
     this.monitorConfKeys.add("agreement.all.outcount_errorlogin");
     this.monitorConfKeys.add("agreement.all.time_enablelogin");
   }

	/**
	 * 校验SCSP用户名密码
	 * 
	 * @param hostID
	 * @param userName
	 * @param password
	 * @return
	 */
   public AuthResult authSCSPUser(Node node, String userName, String password, String hostIP)
   {
     if (node == null) {
       return new AuthResult(11001, "AuthFailed: UserName not found");
     }
     String dbPassword = node.getPassword();
     try {
       dbPassword = dbPassword == null ? null : RSAUtil.getReadableMD5Data(dbPassword.getBytes("iso-8859-1"));
     }
     catch (UnsupportedEncodingException e) {
     }
     if ((dbPassword != null) && (!dbPassword.equals(password))) {
       return new AuthResult(Constants.ResultCode.PASSWORD_ERROR, "AuthFailed: Pasword not matched");
     }
 
     if (!node.checkAccessedIP(hostIP)) {
       String errMsg = "AuthFailed:NE IP not matched,expected is " + node.getIP() + ",but now is " + hostIP;
       return new AuthResult(11004, errMsg);
     }
     return new AuthResult(0, "AuthSuccess");
   }
 
   public AuthResult authAccess(CallParam callParam, String hostIP, boolean checkSrvCtxID, boolean checkIP) {
     if (this.disableAuth) {
       return new AuthResult(AuthResult.SUCCESS, "AuthSuccess");
     }
     Node neNode = SpringContext.getInstance().getConfigService().getNodeByName(callParam.getUserName());
     if (neNode == null) {
       return new AuthResult(Constants.ResultCode.USER_NOT_EXIST, "AuthFailed: NE HostID not found");
     }
     if (checkIP)
     {
       if (!neNode.checkAccessedIP(hostIP)) {
         String errMsg = "AuthFailed:NE IP not matched,expected is " + neNode.getIP() + ",but now is " + hostIP;
 
         return new AuthResult(Constants.DIAMETER_RESULT_UNKNOWN_PEER, errMsg);
       }
     }
     if (checkSrvCtxID) {
       return authCapability(callParam);
     }
     return new AuthResult(0, "AuthSuccess");
   }

	/**
	 * 验证用户是否可以访问服务
	 * 
	 * @param userAddr
	 *            用户地址
	 * @param userName
	 *            用户ID
	 * @param password
	 *            用户密码
	 * @param serviceCtxID
	 *            serviceCtxID
	 * @param checkSrvCtxID
	 *            是否检查checkSrvCtxID
	 * @return AuthResult
	 */
   public AuthResult authWSAccess(CallParam callParam, byte[] dataBytes, boolean pwdIsMD5)
   {
     if (this.disableAuth) {
       return new AuthResult(0, "AuthSuccess");
     }
     XmlMessage request = (XmlMessage)callParam.getRequest();
     Node node = callParam.getFromNode();
     String password = request.getPWD();
     String userName = request.getUID();
     String sign = request.getSIG();
     LoginStatus loginStatus = (LoginStatus)this.tempDeniedNEMap.get(userName);
     if ((loginStatus != null) && 
       (loginStatus.getFailedCount().get() >= this.loginFailedMaxTimes)) {
       if (loginStatus.getLoginTime() + this.tempDeniedSeconds * 1000 >= System.currentTimeMillis()) {
         return new AuthResult(11002, "AuthFailed: Too many failed times ,denied for minites");
       }
       this.tempDeniedNEMap.remove(userName);
       loginStatus = null;
     }
 
		// 需要检查IP
		if (Constants.CheckType.UPI.equals(node.getCheckType()) && !node.checkAccessedIP(request.getIp())) {
			String errMsg = "AuthFailed:NE IP not matched,expected is " + node.getIP() + ",but now is " + request.getIp();
			return new AuthResult(Constants.ResultCode.IP_NOT_MATCH, errMsg);
		}
		// 检查能力是否存在
     CapabilityCtrlBean cap = (CapabilityCtrlBean)SpringContext.getInstance().getConfigService().getCapabilityCtrlMap().get(callParam.getCapability());
     if (cap == null) {
       return new AuthResult(11005, "AuthFailed: No such Capability:" + callParam.getCapability());
     }
 
     if (callParam.getAsynHandler().getProtocolType() == 3) {
       String dbPassword = node.getPassword();
       if (pwdIsMD5) {
         try {
           dbPassword = dbPassword == null ? null : RSAUtil.getReadableMD5Data(dbPassword.getBytes("iso-8859-1"));
         }
         catch (UnsupportedEncodingException e)
         {
         }
       }
			// 检查密码
       if ((dbPassword != null) && (!dbPassword.equals(password))) {
         if (loginStatus == null) {
           loginStatus = new LoginStatus(System.currentTimeMillis());
           this.tempDeniedNEMap.put(userName, loginStatus);
         } else {
           loginStatus.setLoginTime(System.currentTimeMillis());
           loginStatus.getFailedCount().incrementAndGet();
         }
         return new AuthResult(11003, "AuthFailed: Pasword not matched");
       }
     }
     callParam.setSignType(cap.getNumIdiographType());
     if (cap.getNumIdiographType() != 0)
     {
       if (!RSASignService.getInstance().isSignalValid(node.getHostId(), dataBytes, sign, cap.getNumIdiographType()))
         return new AuthResult(11101, "AuthFailed: RSA sign Failed");
     }
     else if (cap.getNumIdiographType() == 2) {
       logger.warn("to wuzhihui , need RSA sign twice.");
     }
     return authCapability(callParam);
   }
	/**
	 * 认证能力访问权限
	 * 
	 * @param cap
	 * @param checkSrvCtxID
	 * @param neNode
	 * @param userName
	 * @return
	 */
	private AuthResult authCapability(CallParam callParam) {
		CapabilityCtrlBean capCtrl = SpringContext.getInstance().getConfigService().getCapabilityCtrlMap().get(callParam.getCapability());
		if (capCtrl == null) {// 能力不存在
			return new AuthResult(Constants.ResultCode.CAPABILITY_NOT_EXIST, "AuthFailed: No such Capability:" + callParam.getCapability());
		}
		capCtrl = SpringContext.getInstance().getConfigService().getCapabilityCtrlMap().get(callParam.getCapability() + callParam.getUserName());
		if (capCtrl == null) {// 用户没有访问权限
			return new AuthResult(Constants.ResultCode.CAPABILITY_NO_AUTH, "AuthFailed: User " + callParam.getUserName() + " not Authorized Capability:" + callParam.getCapability());
		} else {
			callParam.setCapabilityBean(capCtrl);
			return new AuthResult(0, "AuthSuccess");
		}
	}

	@Override
	public Set<String> monitorConfKeys() {
		return this.monitorConfKeys;
	}

	public void init() {
		loadTempDenniedServicePoliy();
	}

	@Override
	public void onConfigUpdated(String updatedKey) {
		loadTempDenniedServicePoliy();

	}

	private void loadTempDenniedServicePoliy() {

		// 默认3次失败，就临时限制2分钟
		this.loginFailedMaxTimes = SpringContext.getInstance().getConfigService().getIntValue(SysParamBean.KEY_AGREEMENT_OutCount_ErrorLogin, 3);
		this.tempDeniedSeconds = SpringContext.getInstance().getConfigService().getIntValue(SysParamBean.KEY_AGREEMENT_Time_EnableLogin, 120);

		logger.info(" loginFailedMaxTimes :" + loginFailedMaxTimes + " tempDeniedSeconds " + tempDeniedSeconds);

	}

	public void setDisableAuth(boolean disableAuth) {
		this.disableAuth = disableAuth;
	}

}
